import os
import cfg
import sys
import pygame
import random
import mediapipe as mp
import cv2
import math
import threading
from modules import *

#手势识别
def vector_2d_angle(v1,v2):
    '''
        求解二维向量的角度
    '''
    v1_x=v1[0]
    v1_y=v1[1]
    v2_x=v2[0]
    v2_y=v2[1]
    try:
        angle_= math.degrees(math.acos((v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5))))
    except:
        angle_ =65535.
    if angle_ > 180.:
        angle_ = 65535.
    return angle_
def hand_angle(hand_):
    '''
        获取对应手相关向量的二维角度,根据角度确定手势
    '''
    angle_list = []
    #---------------------------- thumb 大拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[2][0])),(int(hand_[0][1])-int(hand_[2][1]))),
        ((int(hand_[3][0])- int(hand_[4][0])),(int(hand_[3][1])- int(hand_[4][1])))
        )
    angle_list.append(angle_)
    #---------------------------- index 食指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])-int(hand_[6][0])),(int(hand_[0][1])- int(hand_[6][1]))),
        ((int(hand_[7][0])- int(hand_[8][0])),(int(hand_[7][1])- int(hand_[8][1])))
        )
    angle_list.append(angle_)
    #---------------------------- middle 中指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[10][0])),(int(hand_[0][1])- int(hand_[10][1]))),
        ((int(hand_[11][0])- int(hand_[12][0])),(int(hand_[11][1])- int(hand_[12][1])))
        )
    angle_list.append(angle_)
    #---------------------------- ring 无名指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[14][0])),(int(hand_[0][1])- int(hand_[14][1]))),
        ((int(hand_[15][0])- int(hand_[16][0])),(int(hand_[15][1])- int(hand_[16][1])))
        )
    angle_list.append(angle_)
    #---------------------------- pink 小拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_[0][0])- int(hand_[18][0])),(int(hand_[0][1])- int(hand_[18][1]))),
        ((int(hand_[19][0])- int(hand_[20][0])),(int(hand_[19][1])- int(hand_[20][1])))
        )
    angle_list.append(angle_)
    return angle_list

def h_gesture(angle_list):
    '''
        # 二维约束的方法定义手势
        # left right
    '''
    thr_angle = 65.
    thr_angle_thumb = 53.
    thr_angle_s = 49.
    gesture_str = None
    if 65535. not in angle_list:
   
        if (angle_list[0]>5)  and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "left"  #one
        elif (angle_list[0]>thr_angle_thumb)  and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "right" #two
    return gesture_str

def detect():
    #创建手部检测模型
    mp_drawing = mp.solutions.drawing_utils
    mp_hands = mp.solutions.hands
    hands = mp_hands.Hands(
            static_image_mode=False,#检测视频流
            max_num_hands=1,#检测到手的最大值
            min_detection_confidence=0.75,#手部检测的最小置信度值
            min_tracking_confidence=0.75)#目标踪模型的最小置信度值
    cap = cv2.VideoCapture(0)#调用摄像头
    while True:
        ret,frame = cap.read()#输入流
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frame= cv2.flip(frame,1)#翻转图像
        results = hands.process(frame)
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)

        if results.multi_hand_landmarks:  #手部21个关键点的位置信息
            for hand_landmarks in results.multi_hand_landmarks:
                mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
                hand_local = []
                for i in range(21):
                    #x,y是归一化后的坐标
                    x = hand_landmarks.landmark[i].x*frame.shape[1]
                    y = hand_landmarks.landmark[i].y*frame.shape[0]
                    hand_local.append((x,y))
                if hand_local:
                    angle_list = hand_angle(hand_local)#获取对应手相关向量的二维角度,根据角度确定手势
                    gesture_str = h_gesture(angle_list)#二维约束的方法定义手势
                    try:
                        global hero
                        if gesture_str == 'left':
                            hero.move(cfg.SCREENSIZE, 'left')
                        elif gesture_str == 'right':
                            hero.move(cfg.SCREENSIZE, 'right')
                    except AttributeError as e:
                        print(e)
                    # 图片 添加的文字 位置 字体 字体大小 字体颜色 字体粗细
                    cv2.putText(frame,gesture_str,(0,100),0,1.3,(0,0,255),3)
        cv2.imshow('MediaPipe Hands', frame)
        if cv2.waitKey(1) & 0xFF == ord('q') or pygame.time.get_ticks()>=90000:#按q退出摄像头或者90000ms后自动退出
            break
    cap.release()#摄像头释放



'''游戏初始化'''
def initGame():
    #初始化pygame，设置展示窗口
    pygame.init()
    screen = pygame.display.set_mode(cfg.SCREENSIZE)
    pygame.display.set_caption('终结谷之战！-- 方子涵')
    #加载必要的游戏素材
    game_images = {}
    for key, value in cfg.IMAGE_PATHS.items():
        if isinstance(value, list):
            images = []
            for item in value:images.append(pygame.image.load(item))
            game_images[key] = images
        else:
            game_images[key] = pygame.image.load(value)
    game_sounds = {}
    for key,value in cfg.AUDIO_PATHS.items():
        if key == 'bgm':continue
        game_sounds[key] = pygame.mixer.Sound(value)
    #返回初始化数据
    return screen,game_images,game_sounds

'''主函数'''
def main():
    #退出pygame模块达到重置pygame.time.get_ticks()的目的
    pygame.quit()
    #初始化
    screen,game_images,game_sounds = initGame()
    #加载开始界面
    showStartGameInterface(screen,cfg)
    #播放背景音乐
    pygame.mixer.music.load(cfg.AUDIO_PATHS['bgm'])
    pygame.mixer.music.play(-1, 0.0, 1000)
    #音量大小
    pygame.mixer.music.set_volume(0.3)
    #字体加载
    font = pygame.font.Font(cfg.FONT_PATH, 30)
    #定义hero
    global hero
    hero = Hero(game_images['hero'], position=(364,364,500,600))
    #定义食物组
    food_sprites_group = pygame.sprite.Group()
    generate_food_freq = random.randint(10, 20)
    generate_food_count = 0
    #定义障碍物组
    obstacle_sprites_group = pygame.sprite.Group()
    generate_obstacle_freq = random.randint(70,80)
    generate_obstacle_count = 0
    #当前分数/历史最高分
    score = 0
    highest_score = 0 if not os.path.exists(cfg.HIGHEST_SCORE_RECORD_FILEPATH) else int(open(cfg.HIGHEST_SCORE_RECORD_FILEPATH).read())

    #障碍物得分
    obstacle_score = 3
    #游戏主循环
    clock = pygame.time.Clock()
    while True:
        # --填充背景
        screen.fill(0)
        screen.blit(game_images['background'], (0, 0))
        # --倒计时信息
        countdown_text = 'Count down: ' + str((90000 - pygame.time.get_ticks()) // 60000) + ":" + str((90000 - pygame.time.get_ticks()) // 1000 % 60).zfill(2)
        countdown_text = font.render(countdown_text, True, (0, 0, 0))
        countdown_rect = countdown_text.get_rect()
        countdown_rect.topright = [cfg.SCREENSIZE[0]-15, 5]
        screen.blit(countdown_text, countdown_rect)
        # --按键检测
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        key_pressed = pygame.key.get_pressed()
        if key_pressed[pygame.K_a] or key_pressed[pygame.K_LEFT]:
            hero.move(cfg.SCREENSIZE, 'left')
        if key_pressed[pygame.K_d] or key_pressed[pygame.K_RIGHT]:
            hero.move(cfg.SCREENSIZE, 'right')

        # --随机生成食物
        generate_food_count += 1
        if generate_food_count > generate_food_freq:
            generate_food_freq = random.randint(10, 20)
            generate_food_count = 0
            food = Food(game_images, random.choice(['spiral',] * 10 + ['devil_fruit']), cfg.SCREENSIZE)
            food_sprites_group.add(food)
        # --更新食物
        for food in food_sprites_group:
            if food.update(): food_sprites_group.remove(food)
        # --碰撞检测
        for food in food_sprites_group:
            if pygame.sprite.collide_mask(food, hero):
                game_sounds['get'].play()
                food_sprites_group.remove(food)
                score += food.score
                if score > highest_score: highest_score = score

        # --随机生成障碍物
        generate_obstacle_count += 1
        if generate_obstacle_count > generate_obstacle_freq:
            generate_obstacle_freq = random.randint(80, 100)
            generate_obstacle_count = 0
            obstacle = Obstacle(game_images, random.choice(['shuriKen']), cfg.SCREENSIZE)
            obstacle_sprites_group.add(obstacle)
        #更新障碍物
        for obstacle in obstacle_sprites_group:
            if obstacle.update():obstacle_sprites_group.remove(obstacle)
        # --碰撞检测碰到人就结束
        for obstacle in obstacle_sprites_group:
            if pygame.sprite.collide_mask(obstacle, hero):
                game_sounds['hit'].play()
                obstacle_sprites_group.remove(obstacle)
                obstacle_score = obstacle_score - obstacle.score
                print(obstacle_score)
        if obstacle_score == 0:
            break
        # --画hero
        hero.draw(screen)
        #--画食物
        food_sprites_group.draw(screen)
        # --画障碍物
        obstacle_sprites_group.draw(screen)
        #--显示得分
        score_text = f'Score: {score}, Highest: {highest_score}'
        score_text = font.render(score_text, True, (0, 0, 0))
        score_rect = score_text.get_rect()
        score_rect.topleft = [5, 5]
        screen.blit(score_text, score_rect)
        # --显示击中次数
        life_text = f'Life: {obstacle_score}'
        life_text = font.render(life_text, True, (255, 0, 0))
        life_rect = life_text.get_rect()
        life_rect.topright = [cfg.SCREENSIZE[0]-15,40]
        screen.blit(life_text, life_rect)
        # --判断游戏是否结束
        if pygame.time.get_ticks() >= 90000:
            break
        #--更新屏幕
        pygame.display.flip()
        clock.tick(cfg.FPS)
    #游戏结束，记录最高分并显示游戏结束画面
    fp = open(cfg.HIGHEST_SCORE_RECORD_FILEPATH, 'w')
    fp.write(str(highest_score))
    fp.close()
    if obstacle_score == 0:
        return showEndGameInterface(screen, cfg, score, highest_score)
    else:
        return showEndGameInterface2(screen, cfg, score, highest_score)
    

threading.Thread(target=detect).start()
if __name__ == '__main__':
    while main():
        pass







            
        

